package com.vhall.component.service.gift.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Splitter;
import com.vhall.component.dao.gift.GiftMapper;
import com.vhall.component.dao.gift.GiftMappingMapper;
import com.vhall.component.dao.gift.GiftRecordMapper;
import com.vhall.component.entity.gift.dto.GiftListDataDTO;
import com.vhall.component.entity.gift.dto.GiftPageDTO;
import com.vhall.component.entity.gift.dto.RoomGiftDTO;
import com.vhall.component.entity.gift.entity.Gift;
import com.vhall.component.entity.gift.entity.GiftMapping;
import com.vhall.component.entity.gift.entity.GiftRecord;
import com.vhall.component.entity.gift.vo.*;
import com.vhall.component.entity.income.IncomeCreateDTO;
import com.vhall.component.entity.order.OrderDetailEntity;
import com.vhall.component.entity.page.vo.PageVO;
import com.vhall.component.entity.pay.vo.PaymentParamVO;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.enums.DeleteStatusEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.BeanCopyUtil;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RandomUtil;
import com.vhall.component.plugin.core.enums.SendMessageTypeEnum;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.gift.GiftService;
import com.vhall.component.service.gift.common.Consts;
import com.vhall.component.service.order.IncomeService;
import com.vhall.component.service.order.OrderService;
import com.vhall.component.service.pay.NotifyService;
import com.vhall.component.service.pay.PaymentService;
import com.vhall.component.service.room.RoomInnerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class GiftServiceImpl implements GiftService {

    private static final String LIMIT_1 = "limit 1";
    private static final String CREATOR_ID = "creator_id";

    @Autowired
    private RoomInnerService roomInnerService;
    @Autowired
    private NotifyService notifyService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private PaasService paasService;
    @Autowired
    private IncomeService incomeService;
    @Autowired
    private GiftMapper giftMapper;
    @Autowired
    private GiftMappingMapper giftMappingMapper;
    @Autowired
    private GiftRecordMapper giftRecordMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Value("${paas.host}")
    public String api;
    @Value("${paas.apps.lite.appId}")
    public String appId;
    @Value("${paas.apps.lite.appSecret}")
    public String appSecret;
    @Value("${vhall.pay.fake}")
    private Boolean fakePay;


    @Override
    public GiftRspVo create(GiftAddReqVo reqVo) {
        Gift gift = new Gift();
        BeanUtils.copyProperties(reqVo, gift);
        gift.setSourceStatus(String.valueOf(reqVo.getSourceStatus()));
        gift.setSourceType(String.valueOf(reqVo.getSourceType()));
        gift.setGoodsStatus(1);
        gift.setSwitchStatus(0);
        gift.setAppId(appId);
        gift.setCreatedAt(LocalDateTime.now());
        int insert = giftMapper.insert(gift);
        if (insert == 0) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_ADD_FAILURE);
        }
        GiftRspVo rspVO = new GiftRspVo();
        BeanUtils.copyProperties(gift, rspVO);
        return rspVO;
    }

    @Override
    public Integer delete(GiftDelReqVo reqVo) {
        LambdaUpdateWrapper<Gift> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(Gift::getAppId, appId)
                .eq(Gift::getId, reqVo.getId())
                .set(Gift::getDeletedAt, LocalDateTime.now())
                .set(Gift::getDeleted, 1);
        return giftMapper.update(null, wrapper);
    }

    @Override
    public GiftRspVo update(GiftUpdateReqVo reqVo) {
        LambdaQueryWrapper<Gift> param = Wrappers.lambdaQuery();
        param.eq(reqVo.getId() != null, Gift::getId, reqVo.getId())
                .eq(Gift::getAppId, appId)
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), Gift::getCreatorId, reqVo.getCreatorId())
                .eq(Gift::getDeleted, Consts.NOT_DELETED);
        Gift gift1 = giftMapper.selectOne(param);
        if (gift1 == null) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_NOT_FOUND);
        }

        LambdaUpdateWrapper<Gift> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(reqVo.getId() != null, Gift::getId, reqVo.getId())
                .eq(Gift::getAppId, appId)
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), Gift::getCreatorId, reqVo.getCreatorId())
                .eq(Gift::getDeleted, Consts.NOT_DELETED)
                .set(StringUtils.isNotBlank(reqVo.getName()), Gift::getName, reqVo.getName())
                .set(StringUtils.isNotBlank(reqVo.getImageUrl()), Gift::getImageUrl, reqVo.getImageUrl())
                .set(reqVo.getPrice() != null, Gift::getPrice, reqVo.getPrice())
                .set(Gift::getUpdatedAt, LocalDateTime.now());

        int update = giftMapper.update(null, wrapper);
        if (update == 0) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_EDIT_FAILURE);
        }
        Gift gift = giftMapper.selectById(reqVo.getId());
        GiftRspVo rsp = new GiftRspVo();
        BeanUtils.copyProperties(gift, rsp);
        return rsp;
    }

    @Override
    public Map<String, Object> list(ListReqVo reqVo) {

        if (reqVo.getRoomId() != null) {
            RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVo.getRoomId());
            if (roomInfo == null) {
                throw new BusinessException(BizErrorCode.EMPTY_ROOM);
            }
            reqVo.setCreatorId(roomInfo.getAccountId().toString());
            reqVo.setSourceId(roomInfo.getRoomId());
        }
        int page = reqVo.getPage() != null ? reqVo.getPage() : 1;
        int pageSize = reqVo.getPageSize() != null ? reqVo.getPageSize() : 2;
        int offset = pageSize * (page - 1);
        reqVo.setOffset(offset);
        reqVo.setLimit(pageSize);
        GiftListRspVo res;
        if (reqVo.getRoomId() == null) {
            listDto vo = new listDto();
            BeanUtils.copyProperties(reqVo, vo);
            res = listToPub(vo);
        } else {
            useListDto vo = new useListDto();
            BeanUtils.copyProperties(reqVo, vo);
            res = usedListToPub(vo);
        }
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("total", res.getCount());
        map.put("page", page);
        map.put("last_page", pageSize);
        map.put("list", res.getList());
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MapSaveRspVO mappingSave(MapSaveReqVo reqVo) {
        LambdaQueryWrapper<GiftMapping> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GiftMapping::getAppId, appId)
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), GiftMapping::getCreatorId, reqVo.getCreatorId())
                .eq(StringUtils.isNotBlank(reqVo.getRoomId()), GiftMapping::getRoomId, reqVo.getRoomId())
                .eq(reqVo.getType() != null, GiftMapping::getType, reqVo.getType())
                .eq(GiftMapping::getDeleted, Consts.NOT_DELETED)
                .last(" limit 1");
        GiftMapping giftMap = giftMappingMapper.selectOne(wrapper);
        GiftMapping giftMapping = new GiftMapping();
        BeanUtils.copyProperties(reqVo, giftMapping);
        LocalDateTime now = LocalDateTime.now();
        if (giftMap != null) {
            giftMapping.setId(giftMap.getId());
            giftMapping.setUpdatedAt(now);
            giftMappingMapper.updateById(giftMapping);
        } else {
            giftMapping.setCreatedAt(now);
            giftMapping.setUpdatedAt(now);
            giftMappingMapper.insert(giftMapping);
        }
        MapSaveRspVO rspVO = new MapSaveRspVO();
        BeanUtils.copyProperties(giftMapping, rspVO);
        return rspVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayStatusSetRspVO setPayStatus(SetPayStatusReqVo reqVo) {
        Map<String, Object> res;
        PayStatusSetRspVO payStatus = null;
        if (!fakePay) {
            payStatus = payStatusSetToPub(reqVo);
        } else {
            res = paymentService.getTradeNoCache(reqVo.getTradeNo());
            if (res != null) {
                payStatus = JSON.parseObject(JsonUtil.toJsonString(res), PayStatusSetRspVO.class);
                payStatus.setPayStatus(1);
                reqVo.setThirdPartyTradeNo(reqVo.getTradeNo());
            }
            log.info("Rew_payStatusSet1，data:{}", res);
        }
        if (payStatus == null || !Objects.equals(1, payStatus.getPayStatus())) {
            return null;
        }
        //收入
        OrderDetailEntity da = new OrderDetailEntity();
        da.setRoomId(payStatus.getRoomId());
        da.setAmount(payStatus.getPrice().multiply(new BigDecimal(100)));
        da.setSource(1);
        da.setTradeNo(reqVo.getThirdPartyTradeNo());
        da.setAppId(appId);
        Integer channel = "WEIXIN".equals(reqVo.getChannel()) ? 1 : 2;
        da.setChannel(channel);
        da.setAccountId(Integer.parseInt(payStatus.getReceiverId()));
        log.info("gift-order,data:{},da:{}", payStatus, da);
        orderService.create(da);
        IncomeCreateDTO incomeCreateDTO = new IncomeCreateDTO();
        BeanUtils.copyProperties(da, incomeCreateDTO);
        incomeService.create(incomeCreateDTO);

        //支出
        da.setAmount(payStatus.getPrice());
        da.setStatus(1);
        orderService.create(da);

        HashMap<String, Object> map = new HashMap<>(16);
        map.put("type", SendMessageTypeEnum.GIFT_SEND_SUCCESS.getType());
        map.put("room_id", payStatus.getRoomId());
        map.put("gift_user_id", payStatus.getGiftUserId());
        map.put("gift_user_nickname", payStatus.getGiftUserNickname());
        map.put("gift_user_avatar", payStatus.getGiftUserAvatar());
        map.put("gift_user_name", payStatus.getGiftUserName());
        map.put("gift_user_phone", payStatus.getGiftUserPhone());
        map.put("gift_name", payStatus.getName());
        map.put("gift_price", payStatus.getPrice());
        map.put("gift_image_url", payStatus.getEffectImg());
        map.put("gift_id", payStatus.getGiftId());
        map.put("gift_receiver_id", payStatus.getReceiverId());
        map.put("gift_creator_id", payStatus.getCreatorId());
        Integer number = payStatus.getNumbers() != null ? payStatus.getNumbers() : 0;
        map.put("gift_numbers", number);
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(payStatus.getRoomId());
        paasService.sendMessage(roomInfo.getChannelId(), JsonUtil.toJsonString(map));
        return payStatus;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GiftSendRspVo send(GiftSendReqVo reqVo) {
        RoomJoinsEntityDTO roomJoinInfo = roomInnerService.getRoomJoinInfo(Integer.valueOf(reqVo.getGiftUserId()), reqVo.getRoomId());
        if (roomJoinInfo != null) {
            if (roomJoinInfo.getNickname() != null) {
                reqVo.setGiftUserNickname(roomJoinInfo.getNickname());
            }
            if (roomJoinInfo.getAvatar() != null) {
                reqVo.setGiftUserAvatar(roomJoinInfo.getAvatar());
            }
        }
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVo.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        String s = makeBizOrderNo();
        reqVo.setReceiverId(String.valueOf(roomInfo.getAccountId()));
        reqVo.setSourceId(roomInfo.getRoomId());
        reqVo.setTradeNo(s);
        GiftSendRspVo res = giftSend(reqVo);
        log.info("gift-test,data:{}", res);
        BigDecimal totalFee = res.getPrice() == null ? new BigDecimal(0) : res.getPrice();
        if (reqVo.getNumbers() != null && reqVo.getNumbers() != 0) {
            totalFee = totalFee.multiply(new BigDecimal(reqVo.getNumbers()));
        }

        HashMap<String, Object> payParams = new HashMap<>(16);
        payParams.put("channel", reqVo.getChannel());
        payParams.put("service_code", reqVo.getServiceCode());
        payParams.put("optional", "gift");
        payParams.put("biz_order_no", reqVo.getTradeNo());
        payParams.put("title", "送表情");
        payParams.put("detail", "送表情");
        payParams.put("total_fee", totalFee);
        payParams.put("open_id", reqVo.getOpenId());
        //上报数据收集
        //php还未开发完成
        Object payment = getPayment(payParams);
        res.setPayData(payment);
        return res;
    }

    private GiftSendRspVo giftSend(GiftSendReqVo reqVo) {
        GiftSendRspVo result = sendToPub(reqVo);
        //使用虚拟支付
        if (result.getNumbers() == 0) {
            result.setNumbers(reqVo.getNumbers());
        }
        Map<String, Object> params = BeanUtil.beanToMap(result, true, false);
        paymentService.tradeNoInsertCache(params);
        return result;
    }

    @Override
    public Map<String, Object> useList(UseListReqVo reqVo) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVo.getRoomId());
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        reqVo.setPage(1).setPageSize(100).setCreatorId(roomInfo.getAccountId()).setSourceId(roomInfo.getRoomId());
        Integer offset = reqVo.getPageSize() * (reqVo.getPage() - 1);
        reqVo.setOffset(offset);
        reqVo.setLimit(reqVo.getPageSize());
        useListDto vo = new useListDto();
        BeanUtils.copyProperties(reqVo, vo);
        GiftListRspVo res = usedListToPub(vo);
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("total", res.getCount());
        map.put("page", reqVo.getPage());
        map.put("last_page", reqVo.getPageSize());
        map.put("list", res.getList());
        return map;
    }


    @Override
    public GiftPageDTO<RoomGiftDTO> roomGiftList(RoomGiftListVO vo) {
        Integer pageSize = vo.getPageSize();
        Integer pageIndex = vo.getPage();
        pageIndex = (pageIndex - 1) * pageSize;
        String roomId = vo.getRoomId();
        Integer total = giftMappingMapper
                .selectCount(Wrappers.<GiftMapping>lambdaQuery().eq(GiftMapping::getRoomId, roomId)
                        .eq(GiftMapping::getAppId, appId));
        List<RoomGiftDTO> list = giftMappingMapper.roomGiftMappingList(roomId, appId, pageIndex, pageSize);
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
        GiftPageDTO<RoomGiftDTO> result = new GiftPageDTO<>(pageIndex, pageSize, total, list);
        result.setGiftFrequency(Optional.ofNullable(roomInfo).map(RoomsEntityDTO::getGiftFrequency).orElse(0));
        return result;
    }

    @Override
    public PageVO<GiftListDataDTO> watchGiftList(WatchGiftListReqVo reqVo) {
        LambdaQueryWrapper<Gift> where = Wrappers.lambdaQuery();
        where.eq(Gift::getDeleted, DeleteStatusEnum.NO_DELETE.getStatus()).eq(Gift::getAppId, appId);
        where.last("ORDER BY gift_sort asc ,id asc");
        Integer pageIndex = reqVo.getPage();
        Integer pageSize = reqVo.getPageSize();
        Page<Gift> page = new Page<>(pageIndex, pageSize);
        Page<Gift> quickRepliesEntityPage = giftMapper.selectPage(page, where);
        List<GiftListDataDTO> list = new ArrayList<>();
        if (Objects.nonNull(quickRepliesEntityPage) && quickRepliesEntityPage.getTotal() > 0) {
            list = quickRepliesEntityPage.getRecords().stream().map(entity -> {
                GiftListDataDTO dto = new GiftListDataDTO();
                BeanUtil.copyProperties(entity, dto, false);
                return dto;
            }).collect(Collectors.toList());
        }
        return new PageVO<>(pageIndex, pageSize, (int) page.getTotal(), list);
    }

    @Override
    public GiftListDataDTO getInfoById(Long giftId) {
        Gift gift = giftMapper.selectById(giftId);
        if (gift == null) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_NOT_FOUND);
        }
        GiftListDataDTO giftListDataDTO = new GiftListDataDTO();
        return BeanCopyUtil.copy(gift, giftListDataDTO);
    }

    private Object getPayment(Map<String, Object> payParams) {
        String nonce = MD5.create().digestHex("vss_pay_nonce_str_" + DateUtil.currentSeconds() + "_" + RandomUtil.getRandomNum(0, 99999));
        payParams.put("app_id", appId);
        payParams.put("timestamp", LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
        payParams.put("nonce_str", nonce);
        payParams.put("notify_url", api + "/api/pay/notify");
        if (fakePay) {
            payParams.remove("notify_url");
            payParams.put("sign_at", DateUtil.currentSeconds());
            payParams.put("sign", makeSign(payParams));
            notifyService.payNotify(payParams);
            return "";
        }
        return paymentService.genPayment(BeanUtil.toBean(payParams, PaymentParamVO.class));
    }

    private String makeSign(Map<String, Object> params) {
        params.remove("sign");
        StringBuilder str = new StringBuilder();
        Object[] keyArr = params.keySet().toArray();
        Arrays.sort(keyArr);
        for (Object key : keyArr) {
            String keyStr = (String) key;
            if (params.get(keyStr) != null && !"null".equals(params.get(keyStr))) {
                str.append(key).append(params.get(keyStr));
            }
        }
        str = new StringBuilder(appSecret + str + appSecret);
        return MD5.create().digestHex(str.toString());
    }

    private GiftListRspVo listToPub(listDto reqVo) {

        QueryWrapper<Gift> wrapper = Wrappers.query();
        wrapper.eq("app_id", appId)
                .eq("deleted", Consts.NOT_DELETED)
                .eq(reqVo.getSourceType() != null, "source_type", reqVo.getSourceType())
                .eq(reqVo.getSourceStatus() != null, "source_status", reqVo.getSourceStatus());

        if (null == reqVo.getSourceStatus()) {
            if (reqVo.getCreatorId() != null) {
                wrapper.in(CREATOR_ID, reqVo.getCreatorId(), Consts.DEFAULT_VALUE_CREATOR_ID_SYSTEM);
            }
        } else {
            //source_status=null ，creator_id != null 的情况
            wrapper.eq(reqVo.getCreatorId() != null, CREATOR_ID, reqVo.getCreatorId());
        }
        if (null != reqVo.getGoodsStatus()) {
            wrapper.eq("goods_status", reqVo.getGoodsStatus());
        }

        //分割ids
        if (reqVo.getStatus().equals(Consts.DEFAULT_VALUE_STATUS_YES)) {
            String ids = getMappingIds(reqVo);
            if (StringUtils.isNotBlank(ids)) {
                List<Integer> idList = Splitter.on(",").splitToList(ids).parallelStream().map(s -> {
                    if (StringUtils.isNotBlank(s)) {
                        return Integer.parseInt(s.trim());
                    }
                    return null;
                }).distinct().collect(Collectors.toList());
                wrapper.in(idList.size() > 0, "id", idList);
            }
        }

        wrapper.like(StringUtils.isNotBlank(reqVo.getName()), "name", reqVo.getName());
        GiftListRspVo rspVo = new GiftListRspVo();

        int count = giftMapper.selectCount(wrapper);
        if (count == 0) {
            BeanUtils.copyProperties(reqVo, rspVo);
            rspVo.setCount(count);
            return rspVo;
        }
        // 排序
        List<String> orderByList = Splitter.on(",").splitToList(reqVo.getSortBy());
        orderByList.forEach(s -> {
            if (StringUtils.isBlank(s) || !StringUtils.equalsAnyIgnoreCase(s, "id", "app_id",
                    CREATOR_ID, "creator_nickname", "name", "price", "source_type", "source_status", "bu", "created_at"
                    , "updated_at")) {
                throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
            }
        });
        List<String> orderTypeList = Splitter.on(",").splitToList(reqVo.getSortType());
        orderTypeList.forEach(s -> {
            if (StringUtils.isBlank(s) || !StringUtils.equalsAnyIgnoreCase(s, "asc", "desc")) {
                throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
            }
        });
        for (int i = 0; i < orderByList.size(); i++) {
            int size = orderTypeList.size();
            boolean isAsc = true;
            if (i < size) {
                isAsc = "asc".equalsIgnoreCase(orderTypeList.get(i));
            }
            wrapper.orderBy(true, isAsc, orderByList.get(i));
        }
        wrapper.last(" limit " + reqVo.getOffset() + "," + reqVo.getLimit());
        List<Gift> gifts = giftMapper.selectList(wrapper);
        gifts.forEach(str -> str.setStatus(reqVo.getStatus()));
        BeanUtils.copyProperties(reqVo, rspVo);
        rspVo.setCount(count);
        rspVo.setList(gifts);
        return rspVo;
    }

    private GiftListRspVo usedListToPub(useListDto reqVo) {
        LambdaQueryWrapper<Gift> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Gift::getAppId, appId)
                .eq(reqVo.getGiftType() != null, Gift::getGiftType, reqVo.getGiftType())
                .eq(Gift::getGoodsStatus, 1)
                .eq(Gift::getDeleted, Consts.NOT_DELETED);
        LambdaQueryWrapper<GiftMapping> wrapperSource = Wrappers.lambdaQuery();
        wrapperSource.eq(GiftMapping::getAppId, appId)
                .eq(GiftMapping::getDeleted, Consts.NOT_DELETED)
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), GiftMapping::getCreatorId, reqVo.getCreatorId())
                .eq(StringUtils.isNotBlank(reqVo.getRoomId()), GiftMapping::getRoomId, reqVo.getRoomId())
                .eq(GiftMapping::getType, Consts.DEFAULT_VALUE_TYPE_SOURCE_ID)
                .select(GiftMapping::getGiftId);
        wrapperSource.last(LIMIT_1);
        GiftMapping giftMapping = giftMappingMapper.selectOne(wrapperSource);
        if (giftMapping == null) {
            LambdaQueryWrapper<GiftMapping> wrapperMap = Wrappers.lambdaQuery();
            wrapperMap.eq(GiftMapping::getAppId, appId)
                    .eq(GiftMapping::getDeleted, Consts.NOT_DELETED)
                    .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), GiftMapping::getCreatorId, reqVo.getCreatorId())
                    .eq(GiftMapping::getType, Consts.DEFAULT_VALUE_TYPE_CREATOR_ID)
                    .select(GiftMapping::getGiftId);
            wrapperMap.last(LIMIT_1);
            giftMapping = giftMappingMapper.selectOne(wrapperMap);
        }
        String rowId = giftMapping != null ? giftMapping.getGiftId().toString() : "";
        List<Integer> idList = null;
        if (StringUtils.isNotBlank(rowId)) {
            idList = Splitter.on(",").splitToList(rowId).parallelStream().map(s -> {
                if (StringUtils.isNotBlank(s)) {
                    return Integer.parseInt(s.trim());
                }
                return null;
            }).distinct().collect(Collectors.toList());
        }
        if (idList != null) {
            wrapper.in(Gift::getId, idList);
        } else {
            wrapper.eq(Gift::getSourceStatus, Consts.DEFAULT_VALUE_CREATOR_ID_SYSTEM);
        }
        Integer count = giftMapper.selectCount(wrapper);
        GiftListRspVo rspVo = new GiftListRspVo();
        if (count == Consts.DEFAULT_VALUE_ZERO) {
            BeanUtils.copyProperties(reqVo, rspVo);
            rspVo.setCount(count);
            return rspVo;
        }
        wrapper.last("order by created_at asc limit " + reqVo.getOffset() + " ," + reqVo.getLimit());
        List<Gift> gifts = giftMapper.selectList(wrapper);
        if (gifts.size() > 0) {
            gifts.forEach(str -> {
                Integer status = rowId != null && rowId.startsWith(str.getId().toString()) ? Consts.DEFAULT_VALUE_STATUS_YES : Consts.DEFAULT_VALUE_STATUS_NO;
                str.setStatus(status);
            });
        }
        BeanUtils.copyProperties(reqVo, rspVo);
        rspVo.setCount(count);
        rspVo.setList(gifts);
        return rspVo;
    }

    private PayStatusSetRspVO payStatusSetToPub(SetPayStatusReqVo reqVo) {
        LambdaQueryWrapper<GiftRecord> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GiftRecord::getAppId, appId)
                .eq(StringUtils.isNotBlank(reqVo.getTradeNo()), GiftRecord::getTradeNo, reqVo.getTradeNo())
                .eq(GiftRecord::getDeleted, Consts.NOT_DELETED)
                .last(LIMIT_1);
        GiftRecord record = giftRecordMapper.selectOne(wrapper);
        if (record == null) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_PAY_STATUS_SET_FAILURE);
        }
        GiftRecord giftRecord = new GiftRecord();
        if (!record.getPayStatus().equals(reqVo.getPayStatus())) {
            if (record.getPayStatus() != 1 && reqVo.getPayStatus() == 1) {
                //排行榜赠送者基本信息
                String cacheKey = Consts.REDIS_DB_KEY_GIFT_USER + record.getGiftUserId();
                GiftUserInfoVo userInfo = new GiftUserInfoVo();
                BeanUtils.copyProperties(record, userInfo);
                redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(userInfo));
                //排行榜加入redis
                String rdsKey = Consts.REDIS_DB_KEY_GIFT_RANK + record.getRoomId();
                BigDecimal amount = record.getPrice().multiply(new BigDecimal(record.getNumbers())).setScale(2, RoundingMode.HALF_UP);
                redisTemplate.opsForZSet().incrementScore(rdsKey, record.getGiftUserId(), amount.doubleValue());
            }
            giftRecord.setPayStatus(reqVo.getPayStatus());
            giftRecord.setUpdatedAt(LocalDateTime.now());
            giftRecord.setId(record.getId());
            int update = giftRecordMapper.updateById(giftRecord);
            if (update == 0) {
                throw new BusinessException(BizErrorCode.COMP_GIFT_PAY_STATUS_SET_FAILURE);
            }
        }
        record.setPayStatus(reqVo.getPayStatus());
        PayStatusSetRspVO rsp = new PayStatusSetRspVO();
        BeanUtils.copyProperties(record, rsp);
        return rsp;
    }

    private GiftSendRspVo sendToPub(GiftSendReqVo reqVo) {
        Integer number = reqVo.getNumbers() == null ? 1 : reqVo.getNumbers();
        reqVo.setNumbers(number);
        LambdaQueryWrapper<Gift> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Gift::getAppId, appId)
                .eq(reqVo.getGiftId() != null, Gift::getId, reqVo.getGiftId())
                .eq(Gift::getDeleted, Consts.NOT_DELETED);
        wrapper.last(LIMIT_1);
        Gift gift = giftMapper.selectOne(wrapper);
        if (gift == null) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_SEND_FAILURE);
        }
        GiftRecord record = new GiftRecord();
        BeanUtils.copyProperties(reqVo, record);
        record.setAppId(appId);
        record.setCreatorId(gift.getCreatorId());
        record.setName(gift.getName());
        record.setImageUrl(gift.getImageUrl());
        record.setPrice(gift.getPrice());
        //免费表情直接设为支付成功
        if (gift.getPrice().compareTo(BigDecimal.ZERO) == 0) {
            record.setPayStatus(1);
            //排行榜赠送者基本信息
            String cacheKey = Consts.REDIS_DB_KEY_GIFT_USER + record.getGiftUserId();
            GiftUserInfoVo userInfo = new GiftUserInfoVo();
            BeanUtils.copyProperties(record, userInfo);
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(userInfo));
            //排行榜加入redis
            String rdsKey = Consts.REDIS_DB_KEY_GIFT_RANK + appId + ":" + record.getRoomId();
            redisTemplate.opsForZSet().incrementScore(rdsKey, record.getGiftUserId(), 0);
        }
        LocalDateTime now = LocalDateTime.now();
        record.setCreatedAt(now);
        record.setUpdatedAt(now);
        if (giftRecordMapper.insert(record) == 0) {
            throw new BusinessException(BizErrorCode.COMP_GIFT_SEND_FAILURE);
        }

        GiftSendRspVo rspVo = new GiftSendRspVo();
        BeanUtils.copyProperties(record, rspVo);
        return rspVo;
    }

    private String getMappingIds(listDto reqVo) {
        LambdaQueryWrapper<GiftMapping> wrapperMap = Wrappers.lambdaQuery();
        wrapperMap.eq(StringUtils.isNotBlank(reqVo.getAppId()), GiftMapping::getAppId, reqVo.getAppId())
                .eq(StringUtils.isNotBlank(reqVo.getRoomId()), GiftMapping::getRoomId, reqVo.getRoomId())
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), GiftMapping::getCreatorId, reqVo.getCreatorId())
                .eq(GiftMapping::getDeleted, Consts.NOT_DELETED);
        if (StringUtils.isNotBlank(reqVo.getRoomId())) {
            wrapperMap.eq(GiftMapping::getType, Consts.DEFAULT_VALUE_TYPE_SOURCE_ID);
        } else {
            wrapperMap.eq(GiftMapping::getType, Consts.DEFAULT_VALUE_TYPE_CREATOR_ID);
        }
        wrapperMap.last(LIMIT_1);
        GiftMapping giftMapping = giftMappingMapper.selectOne(wrapperMap);
        if (giftMapping == null) {
            return null;
        }
        return giftMapping.getGiftId().toString();
    }

    private String makeBizOrderNo() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS");
        // 获取1000到9999的随机数
        int random = RandomUtil.getRandomNum(1000, 9999);
        return "GF" + LocalDateTime.now().format(formatter) + random;
    }
}
